<template>
    <div class="sc-upload" :class="{ 'sc-upload-round': round }" :style="style">
        <div v-if="file && file.status != 'success'" class="sc-upload__uploading">
            <div class="sc-upload__progress">
                <el-progress :percentage="file.percentage" :text-inside="true" :stroke-width="16" />
            </div>
            <el-image class="image" :src="file.tempFile" fit="cover"></el-image>
        </div>
        <div v-if="file && file.status == 'success'" class="sc-upload__img">
            <el-image class="image" :src="file.url" :preview-src-list="[file.url]" fit="cover" hide-on-click-modal
                append-to-body :z-index="9999">
                <template #placeholder>
                    <div class="sc-upload__img-slot">
                        Loading...
                    </div>
                </template>
            </el-image>
            <div class="sc-upload__img-actions" v-if="!disabled">
                <span class="del" @click="handleRemove()"><el-icon><el-icon-delete /></el-icon></span>
            </div>
        </div>
        <el-upload v-if="!file" class="uploader" ref="uploaderRef" :auto-upload="cropper ? false : autoUpload"
            :disabled="disabled" :show-file-list="showFileList" :action="action" :name="name" :data="data" :accept="accept"
            :limit="1" :http-request="request" :on-change="change" :before-upload="before" :on-success="success"
            :on-error="error" :on-exceed="handleExceed">
            <slot>
                <div class="el-upload--picture-card">
                    <div class="file-empty">
                        <el-icon>
                            <component :is="icon" />
                        </el-icon>
                        <h4 v-if="title">{{ title }}</h4>
                    </div>
                </div>
            </slot>
        </el-upload>
        <span style="display:none!important"><el-input v-model="files"></el-input></span>
        <el-dialog :title="$t('upload.cut')" draggable v-model="cropperDialogVisible" :width="580" @closed="cropperClosed"
            destroy-on-close>
            <sc-cropper :src="cropperFile.tempCropperFile" :compress="compress" :aspectRatio="aspectRatio"
                ref="cropperRef"></sc-cropper>
            <template #footer>
                <el-button @click="cropperDialogVisible = false">{{ $t('common.cancel') }}</el-button>
                <el-button type="primary" @click="cropperSave">{{ $t('common.save') }}</el-button>
            </template>
        </el-dialog>
    </div>
</template>
<script setup>
import { ref, computed, onMounted, nextTick } from 'vue';
import { ElMessage } from 'element-plus';
import config from '@/config';
import i18n from '@/locales';
// 定义异步组件
const scCropper = defineAsyncComponent(() => import('@/components/scCropper/index.vue'));
// 定义组件的 props
const props = defineProps({
    modelValue: { type: String, default: "" },
    height: { type: Number, default: 148 },
    width: { type: Number, default: 148 },
    title: { type: String, default: "" },
    icon: { type: String, default: "el-icon-plus" },
    action: { type: String, default: "" },
    apiObj: { type: Object, default: () => { } },
    name: { type: String, default: config.filename },
    data: { type: Object, default: () => { } },
    accept: { type: String, default: "image/gif, image/jpeg, image/png" },
    maxSize: { type: Number, default: config.maxSizeFile },
    limit: { type: Number, default: 1 },
    autoUpload: { type: Boolean, default: true },
    showFileList: { type: Boolean, default: false },
    disabled: { type: Boolean, default: false },
    round: { type: Boolean, default: false },
    onSuccess: { type: Function, default: () => { return true } },
    cropper: { type: Boolean, default: false },
    compress: { type: Number, default: 1 },
    aspectRatio: { type: Number, default: NaN }
});
// 定义组件可以发出的事件
const emit = defineEmits(['update:modelValue']);
// 定义响应式引用
const files = ref(''); // 存储文件列表的字符串表示
const file = ref(null); // 当前上传文件的引用
const style = computed(() => ({
    // 根据 props 计算样式
    width: props.width + "px",
    height: props.height + "px"
}));
const cropperDialogVisible = ref(false); // 控制裁剪对话框的显示
const cropperFile = ref(null); // 存储待裁剪的文件
const cropperRef = ref(null); // 裁剪组件的引用
const uploaderRef = ref(null); // 上传组件的引用
// 监听 modelValue 的变化
watch(() => props.modelValue, (val) => {
    // 当 modelValue 变化时，更新 files 和 file
    files.value = val;
    newFile(val);
}, { deep: true });

// 监听 files 的变化
watch(files, (val) => {
    // 当 files 变化时，通知父组件更新 modelValue
    emit('update:modelValue', val);
}, { deep: true });
// 组件挂载后的操作
onMounted(() => {
    // 初始化 files 和 file
    files.value = props.modelValue;
    newFile(props.modelValue);
});
// 新文件处理函数
const newFile = (url) => {

    if (url) {
        file.value = {
            status: "success",
            url: url
        }
    } else {
        file.value = null
    }
};
// 裁剪后保存处理函数
const cropperSave = () => {
    cropperRef.value.getCropFile(file => {
        file.uid = cropperFile.value.uid;
        cropperFile.value.raw = file;

        file.value = cropperFile.value;
        file.value.tempFile = URL.createObjectURL(file.value.raw);
        uploaderRef.value.submit();

    }, cropperFile.value.name, cropperFile.value.type);
    cropperDialogVisible.value = false;
};
// 裁剪对话框关闭时的处理函数
const cropperClosed = () => {
    URL.revokeObjectURL(cropperFile.value.tempCropperFile);
    delete cropperFile.value.tempCropperFile;
};
// 文件移除处理函数
const handleRemove = () => {
    clearFiles();
};
// 清除文件的函数
const clearFiles = () => {
    URL.revokeObjectURL(file.value.tempFile);
    files.value = "";
    file.value = null;
    nextTick(() => {
        uploaderRef.value.clearFiles();
    });
};
// 文件变化处理函数
const change = (file, files) => {
    if (files.length > 1) {
        files.splice(0, 1);
    }
    if (props.cropper && file.status == 'ready') {
        const acceptIncludes = ["image/gif", "image/jpeg", "image/png"].includes(file.raw.type);
        if (!acceptIncludes) {
            ElMessage.warning({
                title: i18n.global.t('error.uploadWaring'),
                message: i18n.global.t('error.uploadFileNotImage'),
            });
            return false;
        }
        cropperFile.value = file;
        cropperFile.value.tempCropperFile = URL.createObjectURL(file.raw);
        cropperDialogVisible.value = true;
        return false;
    }
    file.value = file;
    if (file.status == 'ready') {
        file.tempFile = URL.createObjectURL(file.raw);
    }
};
// 上传前的处理函数
const before = (file) => {
    const acceptIncludes = props.accept.replace(/\s/g, "").split(",").includes(file.type);
    if (!acceptIncludes) {
        ElMessage.warning({
            title: i18n.global.t('error.uploadWaring'),
            message: i18n.global.t('error.uploadFileNotImage'),
        });
        clearFiles();
        return false;
    }
    const maxSize = file.size / 1024 / 1024 < props.maxSize;
    if (!maxSize) {
        ElMessage.warning(i18n.global.t('upload.warningUploadBefore', { maxSize: props.maxSize }));
        clearFiles();
        return false;
    }
};
// 超过文件数量限制的处理函数
const handleExceed = (files) => {
    const file = files[0];
    file.uid = genFileId();
    uploaderRef.value.handleStart(file);
};
// 上传成功的处理函数
const success = (res, file) => {
    //释放内存删除blob
    URL.revokeObjectURL(file.tempFile);
    delete file.tempFile;
    var os = props.onSuccess(res, file);
    if (os != undefined && os == false) {
        nextTick(() => {
            file.value = null;
            files.value = "";
        })
        return false;
    }
    var response = config.parseData(res);
    file.url = response.src;
    files.value = file.url;
};
// 上传失败的处理函数
const error = (err) => {
    nextTick(() => {
        clearFiles();
    })
    ElMessage.error(err);
};
// 自定义上传请求的处理函数
const request = (param) => {
    var apiObj = config.apiObj;
    if (props.apiObj) {
        apiObj = props.apiObj;
    }
    const data = new FormData();
    data.append(param.filename, param.file);
    for (const key in param.data) {
        data.append(key, param.data[key]);
    }
    apiObj.post(data, {
        onUploadProgress: e => {
            const complete = parseInt(((e.loaded / e.total) * 100) | 0, 10)
            param.onProgress({ percent: complete })
        }
    }).then(res => {
        var response = config.parseData(res);
        if (response.code == config.successCode) {
            param.onSuccess(res)
        } else {
            param.onError(response.msg || i18n.global.t('error.unknwon'))
        }
    }).catch(err => {
        param.onError(err)
    })
};
</script>
<style scoped>
	.el-form-item.is-error .sc-upload .el-upload--picture-card {border-color: var(--el-color-danger);}
	.sc-upload .el-upload--picture-card {border-radius: 0;}
	.sc-upload .uploader,.sc-upload:deep(.el-upload) {width: 100%;height: 100%;}
	.sc-upload__img {width: 100%;height: 100%;position: relative;}
	.sc-upload__img .image {width: 100%;height: 100%;}
	.sc-upload__img-actions {position: absolute;top:0;right: 0;display: none;}
	.sc-upload__img-actions span {display: flex;justify-content: center;align-items: center;width: 25px;height:25px;cursor: pointer;color: #fff;}
	.sc-upload__img-actions span i {font-size: 12px;}
	.sc-upload__img-actions .del {background: #F56C6C;}
	.sc-upload__img:hover .sc-upload__img-actions {display: block;}
	.sc-upload__img-slot {display: flex;justify-content: center;align-items: center;width: 100%;height: 100%;font-size: 12px;background-color: var(--el-fill-color-lighter);}
	.sc-upload__uploading {width: 100%;height: 100%;position: relative;}
	.sc-upload__progress {position: absolute;width: 100%;height: 100%;display: flex;justify-content: center;align-items: center;background-color: var(--el-overlay-color-lighter);z-index: 1;padding:10px;}
	.sc-upload__progress .el-progress {width: 100%;}
	.sc-upload__uploading .image {width: 100%;height: 100%;}
	.sc-upload .file-empty {width: 100%;height: 100%;display: flex;justify-content: center;align-items: center;flex-direction: column;}
	.sc-upload .file-empty i {font-size: 28px;}
	.sc-upload .file-empty h4 {font-size: 12px;font-weight: normal;color: #8c939d;margin-top: 8px;}
	.sc-upload.sc-upload-round {border-radius: 50%;overflow: hidden;}
	.sc-upload.sc-upload-round .el-upload--picture-card {border-radius: 50%;}
	.sc-upload.sc-upload-round .sc-upload__img-actions {top: auto;left: 0;right: 0;bottom: 0;}
	.sc-upload.sc-upload-round .sc-upload__img-actions span {width: 100%;}
</style>